I implemented RectCut in Haxe.
I've been working in Go/Typescript for the last few years, and most of my personal projects have been Go since then. Really a great language. But in the last few months I've been getting the eye back in with Haxe, finishing off some projects I started a long time ago. As part of one of them (which maybe I'll talk about some time), I was writing UI layout code. There's a lot of ways to approach this, and honestly, a lot of it is overkill when yo know the shape of what you're trying to accomplish. I'm not building a general purpose layout engine, I'm specifically building a handful of menus and UIs that fit pretty squarely into static-ish layouts. But they should still be relative, screen sizes change etc.
A few weeks back I read the RectCut article above. It's a smart idea - there's actually very little primitives needed to build a basic UI. Very inspired by immediate mode GUIs. And also a good fit for what I was writing. I spent ~an hour implementing it in Haxe. The code I'm sharing is library agnostic, but I'm using it with HaxeFlixel (crazy to return to Flixel 15 years after I last touched it). Haxe is still a great language - the Actionscript 3 follow on that everyone needs.
package;
typedef RectCutRectangle = {
var minX:Float;
var minY:Float;
var maxX:Float;
var maxY:Float;
}
class RectCut {
public static function GetRectangle(width:Float, height:Float, x:Float, y:Float):RectCutRectangle {
return {
minX: x - width / 2,
maxX: x + width / 2,
minY: y - height / 2,
maxY: y + height / 2
};
}
public static function CutLeft(rect:RectCutRectangle, width:Float):RectCutRectangle {
var minX = rect.minX;
rect.minX = Math.max(rect.minX, rect.minX + width);
return {
minX: minX,
minY: rect.minY,
maxX: rect.minX,
maxY: rect.maxY
};
}
public static function CutRight(rect:RectCutRectangle, width:Float):RectCutRectangle {
var maxX = rect.maxX;
rect.maxX = Math.min(rect.maxX, rect.maxX - width);
return {
minX: rect.maxX,
minY: rect.minY,
maxX: maxX,
maxY: rect.maxY
};
}
public static function CutTop(rect:RectCutRectangle, height:Float):RectCutRectangle {
var minY = rect.minY;
rect.minY = Math.max(rect.minY, rect.minY + height);
return {
minX: rect.minX,
minY: minY,
maxX: rect.maxX,
maxY: rect.minY
};
}
public static function CutBottom(rect:RectCutRectangle, height:Float):RectCutRectangle {
var maxY = rect.maxY;
rect.maxY = Math.min(rect.maxY, rect.maxY - height);
return {
minX: rect.minX,
minY: rect.maxY,
maxX: rect.maxX,
maxY: maxY
};
}
public static function AddTop(rect:RectCutRectangle, height:Float):RectCutRectangle {
var minY = rect.minY - height;
return {
minX: rect.minX,
minY: minY,
maxX: rect.maxX,
maxY: rect.minY
};
}
}
And here's a basic example of how I'm using it:
uiLayoutRect = RectCut.GetRectangle(120, 200, FlxG.width / 2, FlxG.height / 2);
topRect = RectCut.CutTop(uiLayoutRect, 60);
leftRect = RectCut.CutLeft(uiLayoutRect, 20);
rightRect = RectCut.CutRight(uiLayoutRect, 20);
bottomRect = RectCut.CutBottom(uiLayoutRect, 20);
var titleText = new FlxSprite();
titleText.loadGraphic(AssetPaths.menutitle__png, false, 750, 295);
titleText.scale.set(0.2, 0.2);
titleText.updateHitbox();
var diffX = titleText.width - (topRect.maxX - topRect.minX);
var rectCenterX = topRect.minX - (diffX / 2);
var rectCenterY = topRect.minY;
titleText.x = rectCenterX;
titleText.y = rectCenterY;
add(titleText);
var playRect = RectCut.CutTop(uiLayoutRect, 20);
playButton = new FlxButton(playRect.minX, playRect.minY, "Play", handlePlayClicked);
add(playButton);
// spacing - this can
RectCut.CutTop(uiLayoutRect, 4);
var exitRect = RectCut.CutTop(uiLayoutRect, 20);
var exitButton = new FlxButton(exitRect.minX, exitRect.minY, "Exit", handlePlayClicked);
add(exitButton);